﻿using System.Threading;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Collections.Generic;

namespace Ivory
{
    public abstract class GameScreen : Screenbase
    {
        protected enum GameMenuPhase {
            Game = 0,
            Menu = 1,
        }

        //Fields
        private bool isLoaded = false;
        protected LoadScreenBase LoadingScreen {get; set;}

        private Dictionary<string, GameMenu> _GameMenus;
        private Dictionary<string, GameMenu> GameMenus
        {
            get { return _GameMenus; }
            set { _GameMenus = value; }
        }

        private string _CurrentGameMenu;
        private GameMenuPhase _MenuPhase;
        private GameMenuPhase MenuPhase
        {
            get { return _MenuPhase; }
            set
            {
                _MenuPhase = value;
                RegisterChangeInPhase();
            }
        }

        //Construct
        public GameScreen(Game game)
            : base(game)
        {
            isLoaded = false; //The game isnt Loaded by default, must be forced
            LoadingScreen = null;
            _MenuPhase = GameMenuPhase.Game;
            _GameMenus = new Dictionary<string, GameMenu>();
        }

        //Methods
        #region #@@ Drawing @@#
        public override void drawScreen(SpriteBatch batch, GameTime time)
        {
            if (isLoaded)
            {
                //InteruptGameUpdates
                if (MenuPhase == GameMenuPhase.Menu)
                {
                    GameMenus[_CurrentGameMenu].drawScreen(batch, time);
                    if (GameMenus[_CurrentGameMenu].InteruptGameRender)
                        return;
                }

                //Render Game
                RenderGame(batch, time);
                RenderUI(batch, time);
            }
            
            else {
                if (LoadingScreen == null)
                    throw new IvoryFrameworkError(Errors.InvalidLoadingScreenException);
                
                LoadingScreen.drawScreen(batch, time);
            }
        }

        public abstract void RenderGame(SpriteBatch batch, GameTime time);
        public abstract void RenderUI(SpriteBatch batch, GameTime time);
        
        #endregion

        #region #@@ Updating @@#
        public override void updateScreen(GameTime time, InputControl input)
        {
            if (isLoaded)
            {
                //InteruptGameUpdates
                if (MenuPhase == GameMenuPhase.Menu) 
                    GameMenus[_CurrentGameMenu].updateScreen(time, input); 
                else 
                    //Update the game logic.
                    UpdateGame(time, input);
                    UpdateUI(time, input); 
                    
            }
            else
            {
                if (LoadingScreen == null)
                    throw new IvoryFrameworkError(Errors.InvalidLoadingScreenException);

                LoadingScreen.updateScreen(time, input);
            }
        }

        public abstract void UpdateGame(GameTime time, InputControl input);
        public abstract void UpdateUI(GameTime time, InputControl input);

        #endregion

        #region #@@ GameMenus @@#
        private void SetPhase(GameMenuPhase state)
        {
            _MenuPhase = state;
        }

        protected void SetStartupPhase(GameMenuPhase state)
        {
            SetPhase(state);
        }
        protected void RegisterMenu(string key, GameMenu menu)
        {
            if (_GameMenus.ContainsKey(key))
                throw new IvoryFrameworkError(Errors.KeyClashException(typeof(GameMenu), key)); 

            GameMenus.Add(key, menu);
        }

        protected void ReturnToGame()
        {
            _MenuPhase = GameMenuPhase.Game;
        }
        protected void SetMenu(string key)
        {
            if (!_GameMenus.ContainsKey(key))
                throw new IvoryFrameworkError(Errors.KeyNotFoundException(key));

            _CurrentGameMenu = key;
            _MenuPhase = GameMenuPhase.Menu;            
        }

        private void RegisterChangeInPhase()
        {
            //fired on phase change
        }
        #endregion

        #region #@@ Loading @@#
        public override void loadScreen()
        {
            //If we get here, we need to re/load
            //Toggle loadScreen on & wait for its signal
            isLoaded = false;
            Thread loadingThread = new Thread(
                new ThreadStart(() =>
                {                    
                    //Call LoadAssets() to load data into the game
                    if (LoadData())
                        this.isLoaded = true;
                    else //If we didnt load properly, report an error
                        throw new IvoryFrameworkError(Errors.AssetLoadingException);
                }));

            loadingThread.Start();
            loadingThread.Join();
        }
        public override void preLoad()
        {
            PreloadAssets();

            this.LoadingScreen.Manager = this.Manager;
            this.LoadingScreen.preLoad();

            foreach (GameMenu menu in _GameMenus.Values)
            {
                menu.Manager = this.Manager;
                menu.preLoad();
            }

            
        }

        public void unload()
        {
            isLoaded = false;
        }

        /// <summary>
        /// Assets are preloaded, logic is loaded on Load screen
        /// </summary>
        /// <returns></returns>
        public abstract bool PreloadAssets();
        public abstract bool LoadData();

        #endregion
    }
}
